#!/bin/sh

#
# Copyright 2024  Odin Kroeger
#
# This file is part of SieveManager.
#
# SieveManager is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# SieveManager is distributed in the hope that it will be useful,
# but WITHOUT ALL WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with SieveManager. If not, see
# <https://www.gnu.org/licenses/>.
#

set -Cefu


#
# Globals
#

progname="$(basename "$0")"
: "${progname:="${0##*/}"}"
readonly progname

lockfile=".$progname.lock"
readonly lockfile

remote="$1"
readonly remote

repodir="$(git rev-parse --show-toplevel)"
readonly repodir


#
# Functions
#

# Terminate children and clean up before exiting
atexit() {
	# shellcheck disable=2319
	__atexit_retval="$?"
	trap '' EXIT HUP INT TERM
	set +e
	# shellcheck disable=2046
	kill -s TERM -- $(jobs -p) -$$ 2>/dev/null
	wait
	eval "${atexit-}"
	unset atexit
	return "$__atexit_retval"
}

# Catch a signal, clean up, and re-raise the signal
catch() {
	caught="${1:?}"
	[ "${catch-}" ] || return 0
	atexit
	trap - "$caught"
	kill -s "$caught" "$$"
}

# Print a message to stderr and exit the process with a non-zero status
err() {
	warn "$@"
	exit 1
}

# Wait to acquire a lock
lock() (
	timeout="${1:?}"
	for i in $(seq 0 "$timeout")
	do
		if ( printf '%d\n' "$$" >"$lockfile"; ) >/dev/null 2>&1
		then
			return
		else
			read -r pid <"$lockfile" || :
			if ! [ "$pid" ] || ! kill -0 -- "$pid" >/dev/null 2>&1
			then
				warn "Removing stale $lockfile ..."
				rm -f .pre-push.lock
			elif [ "$i" -lt "$timeout" ]
			then
				sleep 1
			fi
		fi
	done
	warn "Could not acquire $lockfile in $timeout secs"
	return 1
)

# Remove the lockfile
unlock() (
	if [ -e "$lockfile" ]
	then
		read -r pid <"$lockfile"
		if [ "$pid" ] && [ "$pid" -eq "$$" ]
		then rm -f "$lockfile"
		fi
	fi
)

# Print a warning to stderr
warn() {
	printf '%s: %s\n' "$progname" "$*" >&2
}


#
# Init
#

# Set up clean-up and signal handling
trap atexit EXIT
# shellcheck disable=2064
for sig in HUP INT TERM
do trap "catch $sig" "$sig"
done
caught=

# Check that the repodir is set an accessible
if ! [ "$repodir" ]
then err "Could not locate repository"
fi
cd -P "$repodir" || exit

# Make sure only one instance of pre-push is running at a time
atexit="unlock ; ${atexit-}"
lock 2



#
# Main
#

# Collect filenames
sources=
# shellcheck disable=2034
while read -r localref localhash remoteref remotehash
do
	branch="${remoteref##*/}"

	if [ "$(git ls-remote "$remote" "refs/heads/$branch")" ]
	then path="$remote/$branch"
	else path=
	fi

	# Filesnames should not contain whitespace
	for file in $(git diff --name-only --diff-filter=d "$path" HEAD)
	do
		case $file in
		(*.py) sources="$sources $file"
		esac
	done
	readonly sources
done

# Check code
if [ "$sources" ]
then
	if ! git diff -s --exit-code
	then
		catch=
		git stash -q
		atexit="git stash pop -q; ${atexit-}"
		catch=y
		[ "$caught" ] && kill -s "$caught" "$$"
	fi

	# shellcheck disable=1091
	[ -e bin/activate ] && . ./bin/activate
	make PYLINTARGS=--disable=W0511 sources="${sources# }" lint check
fi

